home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1998 January
/
Macworld (1998-01).dmg
/
Shareware World
/
Comms & Internet
/
HTML mode 2.0 etc.
/
htmlUtils.tcl
< prev
next >
Wrap
Text File
|
1997-09-22
|
71KB
|
2,317 lines
## -*-Tcl-*-
# ###################################################################
# HTML mode - tools for editing HTML documents
#
# FILE: "htmlUtils.tcl"
# created: 96-09-01 13.01.43
# last update: 97-09-20 19.02.03
# Author: Johan Linde
# E-mail: <jl@theophys.kth.se>
# www: <http://bach.theophys.kth.se/~jl/Alpha.html>
#
# Version: 2.0
#
# Copyright 1996, 1997 by Johan Linde
#
# This software may be used freely, and distributed freely, as long as the
# receiver is not obligated in any way by receiving it.
#
# If you make improvements to this file, please share them!
#
# ###################################################################
##
proc htmlUtils.tcl {} {}
#
# Mark file
#
proc parseFuncsHTML {} {
return [htmlMarkFile2 0]
}
proc HTMLMarkFile {} {
htmlMarkFile2 1
message "Marks set."
}
proc htmlMarkFile2 {markfile} {
set pos 0
set exp {<[Hh][1-6][^>]*>}
set exp2 {</[Hh][1-6]>}
while {![catch {search -s -f 1 -r 1 -m 0 -i 0 $exp $pos} rs] &&
![catch {search -s -f 1 -r 1 -m 0 -i 0 $exp2 [lindex $rs 1]} res]} {
set start [lindex $rs 0]
set end [lindex $res 1]
set text [getText $start $end]
# Remove tabs and returns from text.
regsub -all "\[\t\r\]+" $text " " text
# remove all tags from text
set headtext [htmlTagStrip $text]
# Set mark only on one line.
if {$end > [nextLineStart $start]} {
set end [expr [nextLineStart $start] - 1]
}
set indlevel [getText [expr $start + 2] [expr $start + 3]]
if {$indlevel > 0 && $indlevel < 7} {
set lab [string range " " 2 $indlevel]
append lab $lab $indlevel " " $headtext
# Cut the menu item if it's longer than 30 letters, not to make it too long.
if {[string length $lab] > 30} {
set lab "[string range $lab 0 29]…"
}
if {$markfile} {
setNamedMark $lab $start $start $end
} else {
lappend parse $lab [lineStart $start]
}
}
set pos $end
}
if {!$markfile} {return $parse}
}
#
# return positions of tags of including elements, as a list of 5 elements --
# openstart openend closestart closeend elementname.
# Elements without a closing tag are ignored.
# args: point to start search backward from; point which must be enclosed
#
# if any problem, return just {0}
#
proc htmlGetContainer {curPos inclPos} {
set startPos $curPos
set startPos2 $inclPos
set searchFinished 0
message "Searching for enclosing tags…"
while {!$searchFinished} {
# find first tag
set isStartTag 0
while {!$isStartTag} {
if {[catch {search -s -f 0 -r 1 -i 0 -m 0 {<[^<>]+>} $startPos} res]} {
message ""
return {0}
}
set tag1start [lindex $res 0]
set tag1end [lindex $res 1]
# get element name
if {![regexp {<([^ \t\r]+).*>} [getText $tag1start $tag1end] tmp tag]} {
message ""
return {0}
}
# is this a closing tag?
if {[string index $tag 0] != "/"} { set isStartTag 1}
set startPos [expr $tag1start - 1]
}
# find closing tag
set res [htmlGetClosing $tag $tag1end]
set tag2start [lindex $res 0]
set tag2end [lindex $res 1]
# If container enclosed along with us, or there is no closing tag,
# continue searching.
if {![llength $res] || $tag2end < $inclPos} {
set startPos [expr $tag1start - 1]
} else {
set Container "$tag1start $tag1end $tag2start $tag2end"
set searchFinished 1
}
}
message ""
return [concat $Container [string toupper $tag]]
}
#
# return position an opening tag if the first element to the left
# of startPos is an element with only an opening tag, as a list of 3 elements --
# openstart openend elementname.
#
# if any problem, return empty string
#
proc htmlGetOpening {startPos} {
while {1} {
if {[catch {search -s -f 0 -r 1 -i 0 -m 0 {<[^<>]+>} $startPos} res]} {
return
}
set tag1start [lindex $res 0]
set tag1end [lindex $res 1]
# get element name
if {![regexp {<([^ \t\r]+).*>} [getText $tag1start $tag1end] tmp tag]} {
return
}
# is this a closing tag?
if {[string index $tag 0] == "/"} {return}
# comment?
if {[string range $tag 0 2] != "!--"} {break}
set startPos [expr $tag1start - 1]
}
# find closing tag
set res [htmlGetClosing $tag $tag1end]
if {![llength $res] } {
return "$tag1start $tag1end [string toupper $tag]"
} else {
return
}
}
proc htmlGetClosing {tag sPos} {
set x </${tag}>
set sPos2 $sPos
while {1} {
set res [search -s -f 1 -r 1 -i 1 -m 0 -n $x $sPos]
# Found any closing tag.
if {![llength $res]} {break}
# Look for another opening tag of the same element.
set y "<${tag}(\[ \\t\\r\]+|>)"
set res2 [search -s -f 1 -r 1 -i 1 -m 0 -n $y $sPos2]
# Is it further away than the closing tag.
if {![llength $res2] || [lindex $res2 0] > [lindex $res 0]} {break}
# If not, find the next closing tag.
set sPos [lindex $res 1]
set sPos2 [lindex $res2 1]
}
return $res
}
# Change choice of an attribute with pre-defined choices.
proc htmlChangeChoice {} {
set pos [expr [getPos] - 1]
if {[catch {search -s -f 0 -r 1 -i 0 -m 0 {<[^<>]+>} $pos} res] ||
[lindex $res 1] < $pos ||
![regexp {<([^ \t\r>]+)} [eval getText $res] tmp tag] ||
[catch {search -s -f 0 -r 1 -i 0 -m 0 {[ \t\r]+[^=]+=\"?[^\" \t\r>]*\"?} $pos} res1] ||
[lindex $res1 1] < $pos ||
![regexp {([^=]+=)((\"[^\" \t\r]*\")|([^\" \t\r>]*))} [eval getText $res1] tmp attr choice]} {
beep
message "Current position is not at an attribute with choices."
return
}
set pos0 [expr [lindex $res1 0] + [string length $attr]]
set pos1 [expr $pos0 + [string length $choice]]
set choice [string trim $choice \"]
set tag [string toupper $tag]
if {$tag == "INPUT"} {
if {![regexp -nocase {type=(([^\" \t\r>]+)|(\"[^\" \t\r]+\"))} [eval getText $res] tmp tag]} {
beep
message "Current position is not at an attribute with choices."
return
}
set tag [string trim [string toupper $tag] \"]
}
if {$tag == "LI"} {
set ltype [htmlFindList]
if {$ltype == "UL"} {
set tag "LI IN UL"
} elseif {$ltype == "OL"} {
set tag "LI IN OL"
}
}
set attr [string trim [string toupper $attr]]
if {($tag != "OL" && $tag != "LI IN OL") || $attr != "TYPE="} {set choice [string toupper $choice]}
set choices [htmlGetChoices $tag]
foreach c $choices {
if {[string match "${attr}*" $c]} {
lappend matches [string range $c [string length $attr] end]
}
}
if {![info exists matches]} {
beep
message "Current position is not at an attribute with choices."
return
}
if {[set this [lsearch -exact $matches $choice]] < 0} {set this 0}
incr this
if {$this == [llength $matches]} {set this 0}
set this [lindex $matches $this]
if {($tag != "OL" && $tag != "LI IN OL") || $attr != "TYPE="} {set this [htmlSetCase $this]}
replaceText $pos0 $pos1 "\"$this\""
goto [expr ($pos0 + [string length $this] > $pos) ? $pos + 1 : $pos0 + [string length $this] + 1]
}
# ◊◊◊◊ Change below for new system §23 ◊◊◊◊ #
# Save current window and uploads it to the ftp server.
proc htmlSavetoFTPServer {} {
global htmlPasswords HTMLmodeVars ftpSig htmlFTPing
set win [stripNameCount [lindex [winNames -f] 0]]
if {[set this [htmlThisFilePath 4]] == ""} {return}
set home [lindex $this 3]
if {$home == "" && [lindex $this 0] != "file:///"} {set home [htmlInWhichHomePage "[lindex $this 0][lindex $this 1]"]}
if {$home == "" || [lindex $this 4] == "4"} {
alertnote "Current window is not in a home page folder."
return
}
foreach f $HTMLmodeVars(FTPservers) {
if {[lindex $f 0] == $home} {set serv $f}
}
if {![info exists serv]} {
alertnote "No ftp server specified for this home page."
htmlHomePages "[lindex $this 0][lindex $this 1]"
return
}
if {[lindex $serv 3] != ""} {set htmlPasswords($home) [lindex $serv 3]}
if {![info exists htmlPasswords($home)]} {
if {![catch {htmlGetPassword [lindex $serv 1]} pword]} {
set htmlPasswords($home) $pword
} else {
return
}
}
save
set path [lindex $this 2]
if {[lindex $serv 4] != ""} {set path [join [list [lindex $serv 4] $path] /]}
if {![info exists ftpSig] || ![htmlCheckRunning $ftpSig] && [catch {launchBackAppl $ftpSig}]} {
getApplSig "Please locate your ftp application" ftpSig
launchBackAppl $ftpSig
}
incr htmlFTPing
switch $ftpSig {
Arch -
FTCh {AEBuild -r -q -t 30000 '$ftpSig' Arch Stor ---- [makeAlis $win] FTPh "“[lindex $serv 1]”" FTPc "“$path”" ArGU "“[lindex $serv 2]”" ArGp "“$htmlPasswords($home)”"}
Woof {
set path [string range $path 0 [expr [string last / $path] - 1]]
AEBuild -r -q -t 30000 '$ftpSig' PURL PURL ---- [makeAlis $win] dest "“ftp://[lindex $serv 2]:$htmlPasswords($home)@[lindex $serv 1]/$path”"
}
}
}
# To handle the reply from the ftp app.
# I wish this could be done in a better way.
if {![info exists htmlModeIsLoaded] && [info commands handleReply] != "" && [info commands htmlHandleReply] == ""} {rename handleReply htmlHandleReply}
proc handleReply {reply} {
global htmlFTPing htmlPasswords
if {$htmlFTPing} {
incr htmlFTPing -1
if {[regexp {errs:“([^”]+)”} $reply dum err]} {
# Fetch error
if {[regexp {Error: (.*)} $err dum err2]} {set err $err2}
alertnote "Ftp error: $err"
unset htmlPasswords
} elseif {[regexp {'----':(-?[0-9]*)} $reply dum err] && $err != "0"} {
# Anarchie error.
message "Ftp error."
unset htmlPasswords
} else {
message "Document uploaded to ftp server."
}
} else {
htmlHandleReply $reply
}
}
# ◊◊◊◊ end changing for new system §23 ◊◊◊◊ #
proc htmlGetPassword {host} {
set values [dialog -w 300 -h 90 -t "Password for $host:" 10 20 290 30 \
-e "" 10 40 290 42 -b OK 20 60 85 80 -b Cancel 105 60 170 80]
if {[lindex $values 2]} {error "Cancel"}
return [string trim [lindex $values 0]]
}
proc htmlForgetPasswords {} {
global htmlPasswords
message "Passwords forgotten."
unset htmlPasswords
}
# Calculate the total size of a document includes images etc.
proc htmlDocumentSize {} {
# Get path to this window.
if {[set thisURL [htmlThisFilePath 3]] == ""} {return}
set exp1 "<!--|\[ \\t\\n\\r\]+(SRC=|LOWSRC=|DYNSRC=|BACKGROUND=)(\"\[^\">\]+\"|\[^ \\t\\n\\r>\]+)"
set exp2 {/\*|[ \t\r\n]+(url)\(\"?([^\"\)]+)\"?\)}
set commStart1 "<!--"
set commEnd1 "-->"
set commStart2 {/*}
set commEnd2 {*/}
set size 0
set counted {}
set external 0
set notfound 0
for {set i 1} {$i < 3} {incr i} {
set pos 0
set exp [set exp$i]
set commStart [set commStart$i]
set commEnd [set commEnd$i]
while {![catch {search -s -f 1 -i 1 -m 0 -r 1 $exp $pos} res]} {
set restxt [eval getText $res]
# Comment?
if {$restxt == $commStart} {
if {![catch {search -s -f 1 -m 0 -i 0 -r 0 -- $commEnd [lindex $res 1]} res]} {
set pos [lindex $res 1]
continue
} else {
break
}
}
# Get path to link.
regexp -nocase $exp $restxt dum1 dum2 linkTo
set linkTo [htmlURLunEscape [string trim $linkTo \"]]
if {![catch {lindex [htmlPathToFile [lindex $thisURL 0] [lindex $thisURL 1] [lindex $thisURL 2] [lindex $thisURL 3] $linkTo] 0} linkToPath]} {
if {[file exists $linkToPath] && ![file isdirectory $linkToPath]} {
if {[lsearch -exact $counted $linkToPath] < 0} {
getFileInfo $linkToPath arr
incr size $arr(datalen)
lappend counted $linkToPath
}
} else {
set notfound 1
}
} else {
set external 1
}
set pos [lindex $res 1]
}
}
incr size [maxPos]
if {$size > 1000} {
set size "[expr $size /1024] kB"
} else {
append size " bytes"
}
set txt "Total size: $size."
if {$notfound} {append etxt "Some files not found. "}
if {$external} {append etxt "External sources excluded."}
if {$notfound || $external} {append txt " ([string trim $etxt])"}
alertnote $txt
}
#
# dividing line
#
proc htmlCommentLine {} {
global HTMLmodeVars fillColumn
set wordWrap $HTMLmodeVars(wordWrap)
set comStr [htmlCommentStrings]
set prefixString [lindex $comStr 0]
set suffixString [lindex $comStr 1]
set s "===================================================================================="
set l [expr [string length $prefixString] + [string length $suffixString]]
if {$wordWrap} {
set l [expr $fillColumn - $l - 1]
} else {
set l [expr 75 - $l - 1]
}
insertText [htmlOpenCR [htmlFindNextIndent]] $prefixString [string range $s 0 $l] $suffixString "\r"
}
# Removes all tab marks from the current selection (if there is one)
# or the current document, maintaining the cursor position in the
# latter case. Stolen from latexMacros.tcl written by Tom Scavo.
proc htmlRemoveMarks {} {
set subs1 0; set subs2 0; set subs3 0
set pos [getPos]
if {[set start $pos] == [set end [selEnd]]} {
set messageString "document"
set start 0
set end [maxPos]
set text1 [getText $start $pos]
set subs1 [regsub -all {•} $text1 {} text1]
set text2 [getText $pos $end]
set subs2 [regsub -all {•} $text2 {} text2]
append text $text1 $text2
} else {
set messageString "selection"
set text [getText $start $end]
set subs3 [regsub -all {•} $text {} text]
}
if {$subs1 || $subs2 || $subs3} then {
replaceText $start $end $text
if {$messageString == "document"} then {
goto [expr $pos - $subs1]
} else {
set end [getPos]
select $start $end
}
set subs [expr $subs1 + $subs2 + $subs3]
message "$subs tab marks removed from $messageString."
} else {
message "No tab marks found in $messageString."
}
}
#===============================================================================
# Character translation
#===============================================================================
#
# Converting characters to HTML entities.
#
# 1 = < > &
# 0 = áé etc.
proc htmlCharacterstohtml {ltgtamp} {
global htmlSpecialCharacter
global htmlSpecialCapCharacter htmlSpecialSymbCharacter
if {$ltgtamp} {
set charlist {& < >}
} else {
foreach a [array names htmlSpecialCharacter] {
if { $a != "eth" && $a != "thorn" && $a != "y´"} {
lappend charlist $a
}
}
foreach a [array names htmlSpecialCapCharacter] {
if {$a != "ETH" && $a != "THORN" && $a != "Y´"} {
lappend charlist $a
}
}
lappend charlist ¡ ¿
}
set subs1 0; set lett 0
set pos [getPos]
if {[set start $pos] == [set end [selEnd]]} {
if {$ltgtamp && \
[askyesno "There is no selection. Really translate < > & in whole document?"] == "no"} {return}
set messageString "document"
set start 0
set end [maxPos]
set isDoc 1
} else {
set messageString "selection"
set isDoc 0
}
message "Translating…"
set text [getText $start $end]
set tmp $text
set upos $pos
set st $start
if {!$ltgtamp} {
while {[regexp -indices -nocase {<!--[ \t\r]+#(INCLUDE|LASTMODIFIED)[ \t\r]+[^>]+>} $tmp str]} {
set sv [string range $tmp [lindex $str 0] [lindex $str 1]]
if {[expr $st + [lindex $str 1]] < $upos} {
incr pos [expr 17 - [string length $sv]]
} elseif {[expr $st + [lindex $str 0]] < $upos} {
incr pos [expr $st + [lindex $str 0] - $upos]
}
lappend savestr $sv
set tmp [string range $tmp [lindex $str 1] end]
incr st [lindex $str 1]
}
regsub -all -nocase "<!--\[ \t\r\]+#(INCLUDE|LASTMODIFIED)\[ \t\r\]+\[^>\]+>" $text {<!-- #INCLUDE -->} text
}
if {$isDoc} {
set text1 [string range $text 0 [expr $pos - $start - 1]]
set text2 [string range $text [expr $pos - $start] end]
} else {
set text1 $text
}
foreach char $charlist {
if {[info exists htmlSpecialCharacter($char)]} {
set rtext "\\&$htmlSpecialCharacter($char);"
} elseif {[info exists htmlSpecialCapCharacter($char)]} {
set rtext "\\&$htmlSpecialCapCharacter($char);"
} elseif {[info exists htmlSpecialSymbCharacter($char)]} {
set rtext "\\&$htmlSpecialSymbCharacter($char);"
} elseif {$char == ">"} {
set rtext "\\>"
} elseif {$char == "<"} {
set rtext "\\<"
} elseif {$char == "&"} {
set rtext "\\&"
}
set subNum [regsub -all $char $text1 [set rtext] text1]
incr subs1 [expr $subNum * ([string length $rtext] - 2)]
incr lett $subNum
if {$isDoc} {
incr lett [regsub -all $char $text2 [set rtext] text2]
}
}
set text $text1
if {$isDoc} {append text $text2}
if {$lett} {
if {[info exists savestr]} {
set i 0
set tmp ""
while {[regexp -indices -nocase {<!--[ \t\r]+#(INCLUDE|LASTMODIFIED)[ \t\r]+[^>]+>} $text str]} {
append tmp [string range $text 0 [expr [lindex $str 0] - 1]]
append tmp [lindex $savestr $i]
set text [string range $text [expr [lindex $str 1] + 1] end]
incr i
}
set text "$tmp$text"
}
replaceText $start $end $text
if {$isDoc} {
goto [expr $upos + $subs1]
} else {
set end [getPos]
select $start $end
}
}
message "$lett characters translated in $messageString."
}
#
# Converting HTML entities to characters.
#
# 1 = < > &
# 0 = áé etc.
proc htmltoCharacters {ltgtamp} {
global htmlCharacterSpecial
global htmlCapCharacterSpecial
message "Translating…"
if {$ltgtamp} {
set entitylist {"&" "<" ">"}
} else {
foreach a [array names htmlCharacterSpecial] {
if { $a != "eth" && $a != "thorn" && $a != "y´"} {
lappend entitylist "&$a;"
}
}
foreach a [array names htmlCapCharacterSpecial] {
if {$a != "ETH" && $a != "THORN" && $a != "Y´"} {
lappend entitylist "&$a;"
}
}
# ¡ ¿
lappend entitylist "¡" "¿"
}
set subs1 0; set lett 0
set pos [getPos]
if {[set start $pos] == [set end [selEnd]]} {
# Move position to linestart to make sure no letter is split.
set pos [lineStart $pos]
set messageString "document"
set start 0
set end [maxPos]
set isDoc 1
} else {
set messageString "selection"
set isDoc 0
}
set text [getText $start $end]
set tmp $text
set upos $pos
set st $start
if {!$ltgtamp} {
while {[regexp -indices -nocase {<!--[ \t\r]+#(INCLUDE|LASTMODIFIED)[ \t\r]+[^>]+>} $tmp str]} {
set sv [string range $tmp [lindex $str 0] [lindex $str 1]]
if {[expr $st + [lindex $str 1]] < $upos} {
incr pos [expr 17 - [string length $sv]]
} elseif {[expr $st + [lindex $str 0]] < $upos} {
incr pos [expr $st + [lindex $str 0] - $upos]
}
lappend savestr $sv
set tmp [string range $tmp [lindex $str 1] end]
incr st [lindex $str 1]
}
regsub -all -nocase "<!--\[ \t\r\]+#(INCLUDE|LASTMODIFIED)\[ \t\r\]+\[^>\]+>" $text {<!-- #INCLUDE -->} text
}
if {$isDoc} {
set text1 [string range $text 0 [expr $pos - $start - 1]]
set text2 [string range $text [expr $pos - $start] end]
} else {
set text1 $text
}
foreach char $entitylist {
set schar [string range $char 1 [expr [string length $char] - 2]]
if {[info exists htmlCharacterSpecial($schar)]} {
set rtext "$htmlCharacterSpecial($schar)"
} elseif {[info exists htmlCapCharacterSpecial($schar)]} {
set rtext "$htmlCapCharacterSpecial($schar)"
} elseif {$schar == "#161"} {
set rtext ¡
} elseif {$schar == "#191"} {
set rtext ¿
} elseif {$schar == "amp"} {
set rtext "\\&"
} elseif {$schar == "lt"} {
set rtext "<"
} elseif {$schar == "gt"} {
set rtext ">"
}
set subNum [regsub -all $char $text1 $rtext text1]
incr subs1 [expr $subNum * ([string length $char] - 1)]
incr lett $subNum
if {$isDoc} {
incr lett [regsub -all $char $text2 $rtext text2]
}
}
set text $text1
if {$isDoc} {append text $text2}
if {$lett} {
if {[info exists savestr]} {
set i 0
set tmp ""
while {[regexp -indices -nocase {<!--[ \t\r]+#(INCLUDE|LASTMODIFIED)[ \t\r]+[^>]+>} $text str]} {
append tmp [string range $text 0 [expr [lindex $str 0] - 1]]
append tmp [lindex $savestr $i]
set text [string range $text [expr [lindex $str 1] + 1] end]
incr i
}
set text "$tmp$text"
}
replaceText $start $end $text
if {$isDoc} {
goto [expr $upos - $subs1]
} else {
set end [getPos]
select $start $end
}
}
message "$lett characters translated in $messageString."
}
#===============================================================================
# HTML character entities
#===============================================================================
proc htmlAddCommonChars {} {
global modifiedModeVars HTMLmodeVars htmlSpecialCharacter htmlCapCharSpecMenu
global htmlSpecialSymbCharacter
set commonChars $HTMLmodeVars(commonChars)
set htmlCharacters [lsort [array names htmlSpecialCharacter]]
set htmlCapCharacters [lsort [array names htmlCapCharSpecMenu]]
set htmlSymbCharacters [lsort [array names htmlSpecialSymbCharacter]]
set htmlAllCharacters [concat $htmlCharacters $htmlCapCharacters $htmlSymbCharacters]
if {![catch {listpick -l -p "Select chars for the commonly used char list" \
$htmlAllCharacters} newchars]} {
set dirty 0
foreach c $newchars {
if {[lsearch -exact $commonChars $c] < 0} {
set dirty 1
set commonChars [lsort [lappend commonChars $c]]
}
}
if {$dirty} {
lappend modifiedModeVars {commonChars HTMLmodeVars}
set HTMLmodeVars(commonChars) $commonChars
htmlRebuildMenu "Rebuiding HTML menu…"
message "New characters added to the common list."
}
}
}
proc htmlDefaultCommonChars {} {
global modifiedModeVars HTMLmodeVars
if {[askyesno "Revert to default common characters?"] == "yes"} {
set HTMLmodeVars(commonChars) $HTMLmodeVars(defaultCommonChars)
lappend modifiedModeVars {commonChars HTMLmodeVars}
htmlRebuildMenu "Rebuiding HTML menu…"
message "Common character list reverted to default."
}
}
proc htmlClearCommonChars {} {
global modifiedModeVars HTMLmodeVars
if {[askyesno "Remove all common characters?"] == "yes"} {
set HTMLmodeVars(commonChars) {}
lappend modifiedModeVars {commonChars HTMLmodeVars}
htmlRebuildMenu "Rebuiding HTML menu…"
message "Common character list cleared."
}
}
#
# Insert special character entity
#
proc htmlInsertCharacter {char} {
global htmlSpecialCharacter htmlCapCharSpecMenu htmlSpecialSymbCharacter
if {[isSelection]} { deleteSelection }
foreach c [list SpecialCharacter CapCharSpecMenu SpecialSymbCharacter] {
if {[info exists html${c}($char)]} {
insertText &[set html${c}($char)]\;
}
}
}
#===============================================================================
# General Commands
#===============================================================================
# remove containing tags
proc htmlUntagandSelect {} {htmlUntag 1}
proc htmlUntag {{selectit 0}} {
set curPos [getPos]
set tags [htmlGetContainer $curPos [selEnd]]
if {[llength $tags] < 5} {
alertnote "Cannot decide on enclosing tags."
return
}
# delete them
replaceText [lindex $tags 0] [lindex $tags 3] \
[getText [lindex $tags 1] [lindex $tags 2]]
if {$selectit} {
select [lindex $tags 0] \
[expr [lindex $tags 2] - [lindex $tags 1] + [lindex $tags 0]]
} else {
if {$curPos < [lindex $tags 1]} {set curPos [lindex $tags 1]}
if {$curPos > [lindex $tags 2]} {set curPos [lindex $tags 2]}
goto [expr $curPos - [lindex $tags 1] + [lindex $tags 0]]
}
message "[lindex $tags 4] deleted."
}
# select container, like Balance (cmd-B)
proc htmlSelectinContainer {} {htmlSelectContainer 1}
proc htmlSelectContainer {{inside 0}} {
set start [getPos]
if {$start != 0 &&
![catch {getText $start [expr $start + 2]} lookingAt] &&
$lookingAt != "</" &&
[string range $lookingAt 0 0] == "<"} {
incr start -1
}
set tags [htmlGetContainer $start [selEnd]]
if {[llength $tags] == 5} {
if {$inside} {
select [lindex $tags 1] [lindex $tags 2]
} else {
select [lindex $tags 0] [lindex $tags 3]
}
message "[lindex $tags 4] selected."
} else {
beep
message "Cannot decide on enclosing tags."
}
}
# Select an opening tag, or remove it, of an element without a closing tag.
proc htmlRemoveOpening {} {htmlSelectOpening 1}
proc htmlSelectOpening {{remove 0}} {
set begin [getPos]
# back up one if possible and selection is wanted.
if {$begin >0 && !$remove} {incr begin -1}
set tag [htmlGetOpening $begin]
if {[llength $tag] == 3} {
if {$remove} {
deleteText [lindex $tag 0] [lindex $tag 1]
if {$begin < [lindex $tag 1]} {set begin [lindex $tag 1]}
goto [expr $begin - [lindex $tag 1] + [lindex $tag 0]]
message "[lindex $tag 2] deleted."
} else {
select [lindex $tag 0] [lindex $tag 1]
message "[lindex $tag 2] selected."
}
} else {
if {$remove} {
alertnote "Cannot find opening tag."
} else {
beep
message "Cannot find opening tag."
}
}
}
# Called by cmd-double-click.
# Change attributes if click on a tag.
proc htmlChangeDblClick {} {
set pos [getPos]
if {[catch {search -s -f 0 -r 1 -i 0 -m 0 {<[^<>]+>} $pos} res] ||
[lindex $res 1] < $pos} {return}
set txt [getText [expr [lindex $res 0] + 1] [expr [lindex $res 1] - 1]]
if {[string index [set tag [lindex $txt 0]] 0] == "/" || $tag == "!--"} {return}
if {[set newTag [htmlChangeElement $txt [string toupper $tag] [lindex $res 0]]] != ""} {
replaceText [lindex $res 0] [lindex $res 1] $newTag
}
}
# Change an existing element.
proc htmlChangeContainer {} {
set tag [htmlGetContainer [getPos] [selEnd]]
if {[llength $tag] == 5} {
set newTag [htmlChangeElement [getText [expr [lindex $tag 0] + 1] \
[expr [lindex $tag 1] - 1]] [lindex $tag 4] [lindex $tag 0]]
if {[string length $newTag]} {
replaceText [lindex $tag 0] [lindex $tag 1] $newTag
}
} else {
alertnote "Cannot decide on enclosing tags."
}
}
proc htmlChangeOpening {} {
set tag [htmlGetOpening [getPos]]
if {[llength $tag] == 3} {
set newTag [htmlChangeElement [getText [expr [lindex $tag 0] + 1] \
[expr [lindex $tag 1] - 1]] [lindex $tag 2] [lindex $tag 0]]
if {[string length $newTag]} {
replaceText [lindex $tag 0] [lindex $tag 1] $newTag
}
} else {
alertnote "Cannot find opening tag."
}
}
#
# Exstracts all attributes to a element from a list, and puts up a dialog window
# where the user can change the attributes.
#
proc htmlChangeElement {tag elem {wrPos 0}} {
global htmlColorAttr htmlURLAttr HTMLmodeVars
global htmluserColorname htmlColorNumber htmlPackageToUse
global htmlElemAttrOptional1 htmlElemAttrOptional3
global htmlElemEventHandler1 htmlWindowAttr htmlPlugins
global htmlSpecURL htmlSpecColor htmlSpecWindow
# Remove tabs and returns from list.
regsub -all "\[\t\r\]+" $tag " " tag
# Remove element name.
set tagelem [lindex $tag 0]
set tag [string range $tag [string length $tagelem] end]
set attrs ""
set attrVals ""
# Exstract the attributes.
while {[regexp {[ ]+([^ "]+"[^"]*"|[^ "]+)} $tag thisatt]} {
set tag [string range $tag [string length $thisatt] end]
set thisatt [htmlRemoveQuotes $thisatt]
lappend attrs [string trim [lindex $thisatt 0]]
lappend attrVals [lindex $thisatt 1]
}
# All INPUT elements are defined differently. Must extract TYPE.
if {$elem == "INPUT"} {
set typeIndex [lsearch -exact [string toupper $attrs] "TYPE="]
if {$typeIndex >= 0 } {
set elem [string toupper [lindex $attrVals $typeIndex]]
# Remove TYPE attribute from list.
set attrs [lreplace $attrs $typeIndex $typeIndex]
set attrVals [lreplace $attrVals $typeIndex $typeIndex]
set used "INPUT TYPE=\"${elem}\""
} else {
beep
message "INPUT element without a TYPE attribute."
return
}
} else {
set used $elem
}
# If EMBED element, choose which
if {$elem == "EMBED" && $htmlPackageToUse == 1} {
if {[catch {listpick -p "Which plug-in?" [lsort $htmlPlugins]} elem] || ![string length $elem]} {return}
}
# If LI element, check in which list.
if {$elem == "LI"} {
set ltype [htmlFindList]
if {$ltype == "UL"} {
set elem "LI IN UL"
} elseif {$ltype == "OL"} {
set elem "LI IN OL"
}
}
set eventText ""
# JavaScript event handlers. Extension package only.
set eventHandler [string toupper [htmlGetEvent $elem]]
# Remove event handler from attributes list,
# if they should not be included, and save them to put them back later.
set attrsToupper [string toupper $attrs]
if {!$HTMLmodeVars(inclEventHandler)} {
foreach ev $eventHandler {
set evIndex [lsearch -exact $attrsToupper $ev]
if {$evIndex >=0} {
append eventText " " [lindex $attrs $evIndex] \
[htmlAddQuotes [lindex $attrVals $evIndex]]
set attrs [lreplace $attrs $evIndex $evIndex]
set attrVals [lreplace $attrVals $evIndex $evIndex]
set attrsToupper [lreplace $attrsToupper $evIndex $evIndex]
}
}
}
set attrs $attrsToupper
# Element known by HTML mode?
if {![info exists htmlElemAttrOptional${htmlPackageToUse}($elem)]} {
alertnote "Unknown element: $elem"
return
}
set useBig $HTMLmodeVars(changeInBigWindows)
set optatts [htmlGetOptional $elem]
set alloptatts [htmlGetOptional $elem 1]
set reqatts [htmlGetRequired $elem]
if {$HTMLmodeVars(useAttsApplyToDialogs) || !$useBig} {
set allAttrs [htmlGetUsed $elem $reqatts $optatts]
} else {
set allAttrs [concat $reqatts $optatts]
}
set reallyAllAtts [concat $reqatts $alloptatts]
set choices [htmlGetChoices $elem]
set numAttrs [htmlGetNumber $elem]
set errText ""
# Check if there are some unknown attributes.
set notUsedAtts ""
foreach a $optatts {
if {[lsearch -exact $allAttrs $a] < 0} {
lappend notUsedAtts $a
}
}
set hiddenAtts ""
foreach a $alloptatts {
if {[lsearch -exact $optatts $a] < 0} {
lappend hiddenAtts $a
}
}
# First check if one which is normally not used is used.
set addNotUsed 0
foreach a $attrs {
if {[lsearch -exact $allAttrs $a] < 0 && [lsearch -exact $reallyAllAtts $a] >= 0} {
append allAttrs " $notUsedAtts"
set addNotUsed 1
break
}
}
# then check some hidden one is used
set addHidden 0
foreach a $attrs {
if {[lsearch -exact $allAttrs $a] < 0 && [lsearch -exact $reallyAllAtts $a] >= 0} {
append allAttrs " $hiddenAtts"
set addHidden 1
break
}
}
# Add event handlers.
if {[string length $eventHandler]} {append allAttrs " " $eventHandler}
# finally check if some is unknown
foreach a $attrs {
if {[lsearch -exact $allAttrs $a] < 0} {
lappend errText "Unknown attribute: $a"
}
}
# Does this element have any attributes?
if {![llength $reallyAllAtts]} {
if {[llength $errText]} {
if {[askyesno "$elem has no attributes. Remove the ones in the text?"] == "no"} {
return
} else {
return [htmlSetCase <$elem>]
}
} else {
beep
message "$elem has no attributes."
return
}
}
# Add something if all attrs are hidden.
if {![llength $allAttrs]} {
if {[llength $notUsedAtts]} {
set allAttrs $notUsedAtts
set addNotUsed 1
} else {
set allAttrs $hiddenAtts
set addNotUsed 1
set addHidden 1
}
}
set values ""
# Add two dummy elements for OK and Cancel buttons.
if {$useBig} {set values {0 0}}
# Build a list with attribute vales.
foreach a $allAttrs {
set attrIndex [lsearch -exact $attrs $a]
if {$attrIndex >= 0 } {set aval [lindex $attrVals $attrIndex]}
set a2 [string trimright $a =]
if {[string index $a [expr [string length $a] - 1]] != "="} {
# Flag
if {$attrIndex >= 0} {
lappend values 1
} else {
lappend values 0
}
} elseif {([lsearch -exact $htmlURLAttr $a] >= 0 && [lsearch -exact $htmlSpecURL "${elem}!=$a2"] < 0) || \
[lsearch -exact $htmlSpecURL "${elem}=$a2"] >= 0} {
# URL
if {$attrIndex >= 0} {
set aval [htmlURLunEscape $aval]
htmlAddToCache URLs $aval
if {$useBig} {
lappend values "" $aval 0
} else {
lappend values $aval
}
} else {
if {$useBig} {
lappend values "" "No value" 0
} else {
lappend values ""
}
}
} elseif {([lsearch -exact $htmlColorAttr $a] >= 0 && [lsearch -exact $htmlSpecColor "${elem}!=$a2"] < 0) || \
[lsearch -exact $htmlSpecColor "${elem}=$a2"] >= 0} {
# Color
if {$attrIndex >= 0} {
set aval [htmlCheckColorNumber $aval]
if {$aval == 0} {
lappend errText "$a: Invalid color number."
if {$useBig} {
lappend values "" "No value" 0
} else {
lappend values ""
}
} elseif {[info exists htmluserColorname($aval)]} {
if {$useBig} {
lappend values "" $htmluserColorname($aval) 0
} else {
lappend values $htmluserColorname($aval)
}
} elseif {[info exists htmlColorNumber($aval)]} {
if {$useBig} {
lappend values "" $htmlColorNumber($aval) 0
} else {
lappend values $htmlColorNumber($aval)
}
} else {
if {$useBig} {
lappend values $aval "No value" 0
} else {
lappend values $aval
}
}
} else {
if {$useBig} {
lappend values "" "No value" 0
} else {
lappend values ""
}
}
} elseif {([lsearch -exact $htmlWindowAttr $a] >= 0 && [lsearch -exact $htmlSpecWindow "${elem}!=$a2"] < 0) || \
[lsearch -exact $htmlSpecWindow "${elem}=$a2"] >= 0} {
# Window
if {$attrIndex >= 0} {
htmlAddToCache windows $aval
if {$useBig} {
lappend values "" $aval
} else {
lappend values $aval
}
} else {
if {$useBig} {
lappend values "" "No value"
} else {
lappend values ""
}
}
} elseif {[lsearch $numAttrs "${a}*"] >= 0} {
# Number
if {$attrIndex >= 0} {
set numcheck [htmlCheckAttrNumber $elem $a $aval]
if {$numcheck == 1} {
lappend values $aval
} else {
lappend errText "$a: $numcheck"
lappend values ""
}
} else {
lappend values ""
}
} elseif {[lsearch $choices "${a}*"] >= 0} {
# Choices
if {$attrIndex >= 0} {
set match ""
if {!(($elem == "OL" || $elem == "LI IN OL") && $a == "TYPE=")} {
set aval [string toupper $aval]
}
foreach w $choices {
if {$w == "${a}${aval}"} {
set match $aval
}
}
if {[string length $match]} {
lappend values $match
} else {
lappend errText "$a: Unknown choice, $aval."
lappend values ""
}
} else {
lappend values ""
}
} elseif {$attrIndex >= 0} {
# Any other
lappend values $aval
} else {
lappend values ""
}
}
# If invalid attributes, continue?
if {[llength $errText] && ![htmlErrorWindow "$elem not well-defined" $errText 1]} {
return
}
if {$useBig} {
set r [htmlOpenElemWindow $used $elem [lindex [posToRowCol $wrPos] 1] $values $addNotUsed $addHidden $wrPos]
} else {
set r [htmlOpenElemStatusBar $used $elem [lindex [posToRowCol $wrPos] 1] $values $addNotUsed $addHidden $wrPos]
}
# Put back event handlers. Empty string means "Cancel", do nothing.
if {[string length $r]} {
set r "[string range $r 0 [expr [string length $r] - 2]]$eventText>"
}
return $r
}
# Removes all tags in a selection or the whole document.
proc htmlRemoveTags {} {
if {![isSelection]} {
if {[set ync [askyesno -c "Put text without tags in a new window?"]] == "cancel"} {return}
set txt [htmlTagStrip [getText 0 [maxPos]]]
if {$ync == "yes"} {
new
insertText $txt
} else {
replaceText 0 [maxPos] $txt
}
} else {
replaceText [getPos] [selEnd] [htmlTagStrip [getSelect]]
}
}
# Put quotes around all attributes
proc htmlQuoteAllAttributes {} {
set pos [getPos]
if {[isSelection]} {
set start [getPos]
set end [selEnd]
} else {
set start 0
set end [maxPos]
}
set text [getText $start $end]
while {[regexp -indices {<!--|<[^<>]+>} $text tag]} {
append newtext [string range $text 0 [lindex $tag 0]]
set this [string range $text [expr [lindex $tag 0] + 1] [lindex $tag 1]]
set text [string range $text [expr [lindex $tag 1] + 1] end]
if {$this == "!--"} {
if {[regexp -indices -- {-->} $text commend]} {
append newtext $this[string range $text 0 [lindex $commend 1]]
set text [string range $text [expr [lindex $commend 1] + 1] end]
} else {
append newtext $text
set text ""
}
} else {
regsub -all "(\[ \t\r\]+\[^=\]+=)(\[^ >\"\t\r\]+)" $this {\1"\2"} newtag
append newtext $newtag
}
}
append newtext $text
replaceText $start $end $newtext
goto $pos
}
# opens the manual in the browser.
proc htmlHelp {} {
global HOME HTMLmodeVars modifiedModeVars browserSig
switch $HTMLmodeVars(manualStartPage) {
0 {set start HTMLmanual.html}
1 {set start text:TableOfContents.html}
2 {set start text:HTMLmanualFrames.html}
}
set path "$HTMLmodeVars(manualFolder):$start"
if {![file exists $path]} {
if {![catch {htmlGetDir "Locate manual"} folder]} {
set path "$folder:$start"
if {![file exists $path]} {
alertnote "Folder doesn't contain the HTML manual."
return
}
set HTMLmodeVars(manualFolder) $folder
lappend modifiedModeVars {manualFolder HTMLmodeVars}
} else {
return
}
}
htmlSendWindow $path
if {!$HTMLmodeVars(browseInForeground)} { switchTo '$browserSig' }
}
#
# launch a viewer and pass this window to it
#
proc htmlSendWindow {{path ""}} {
global HTMLmodeVars browserSig
if {$path == ""} {
set path [stripNameCount [car [winNames -f]]]
if {[winDirty]} {
if {$HTMLmodeVars(saveWithoutAsking) || [set ask [askyesno -c "Save '[file tail $path]'?"]] == "yes"} {
save
} elseif {$ask == "cancel"} {
return
} elseif {![file exists $path]} {
alertnote "Can't send window to browser."
return
}
}
# Get path again, in case it was Untitled before.
set path [stripNameCount [car [winNames -f]]]
}
if {![info exists browserSig] && [catch {getFileSig [icGetPref -t 1 Helper•http]} browserSig]} {set browserSig MOSS}
if {![htmlCheckRunning $browserSig] && [catch {launchBackAppl $browserSig}]} {
getApplSig "Please locate your web browser" browserSig
launchBackAppl $browserSig
}
# MSIE opens the file in a new window unless an open URL event is used.
# Cyberdog opens the text file unless an open URL event is used.
if {$browserSig == "MSIE" || $browserSig == "dogz"} {
set path [htmlURLescape $path 1]
regsub -all : $path / path
set flgs ""
if {$browserSig == "MSIE"} {set flgs "FLGS 1"}
eval AEBuild '$browserSig' WWW! OURL "----" "“file:///$path”" $flgs
} else {
sendOpenEvent noReply '$browserSig' $path
}
if {$HTMLmodeVars(browseInForeground)} { switchTo '$browserSig' }
}
#===============================================================================
# Caches
#===============================================================================
proc htmlCleanUpCache {cache} {
global HTMLmodeVars
global modifiedModeVars
set URLs $HTMLmodeVars($cache)
if {![llength $URLs]} {
alertnote "No $cache are cached."
return
}
set urlnumber [llength $URLs]
set screenHeight [lindex [getMainDevice] 3]
set maxLines [expr ($screenHeight - 160) / 20]
set pages [expr ($urlnumber - 1) / $maxLines ]
set thispage 0
for {set i 0} {$i < $urlnumber} {incr i} {
lappend URLsToSave 1
}
set thisbox $URLsToSave
while {1} {
if {$thispage < $pages} {
set thisurlnumber $maxLines
} else {
set thisurlnumber [expr ($urlnumber - 1 ) % $maxLines + 1]
}
set height [expr 75 + $thisurlnumber * 20]
set box "-w 440 -h $height -b OK 20 [expr $height - 30] 85 [expr $height - 10] \
-b Cancel 100 [expr $height - 30] 165 [expr $height - 10] \
-b {Uncheck all} 180 [expr $height - 30] 265 [expr $height - 10] \
-t {Uncheck the $cache you want to remove} 10 10 440 30 "
if {$thispage < $pages} {
lappend box -b "More…" 280 [expr $height - 30] 345 [expr $height - 10]
}
if {$thispage > 0} {
lappend box -b "Back…" 360 [expr $height - 30] 425 [expr $height - 10]
}
set hpos 30
set thisURLs [lrange $URLs [expr $thispage * $maxLines] \
[expr $thispage * $maxLines + $maxLines - 1]]
set i 0
foreach url $thisURLs {
lappend box -c $url [lindex $thisbox $i] 10 $hpos 430 [expr $hpos + 15]
incr i
incr hpos 20
}
set thisbox [eval [concat dialog $box]]
if {[lindex $thisbox 1]} {
# cancel
return
} elseif {[lindex $thisbox 2]} {
# uncheck all
set thisbox {}
for {set i 0} {$i < [llength $thisbox]} {incr i} {
lappend thisbox 0
}
} else {
if {$pages == 0} {
set ll 3
} elseif {$thispage == 0 || $thispage == $pages} {
set ll 4
} else {
set ll 5
}
set URLsToSave [eval [concat lreplace [list $URLsToSave] [expr $thispage * $maxLines] \
[expr $thispage * $maxLines + $maxLines - 1] [lrange $thisbox $ll end]]]
if {[lindex $thisbox 0]} {
# OK
break
} elseif {$thispage < $pages && [lindex $thisbox 3]} {
# more
incr thispage 1
set thisbox [lrange $URLsToSave [expr $thispage * $maxLines] \
[expr $thispage * $maxLines + $maxLines - 1]]
} else {
# back
incr thispage -1
set thisbox [lrange $URLsToSave [expr $thispage * $maxLines] \
[expr $thispage * $maxLines + $maxLines - 1]]
}
}
}
set newurls {}
for {set i 0} {$i < $urlnumber} {incr i} {
if {[lindex $URLsToSave $i]} {
lappend newurls [lindex $URLs $i]
}
}
set HTMLmodeVars($cache) $newurls
lappend modifiedModeVars [list $cache HTMLmodeVars]
if {![llength $newurls]} {htmlEnable$cache off}
}
proc htmlSelScrapToURL {sel msg1 msg2} {
set newurl [htmlURLunEscape [string trim [eval get$sel]]]
# Convert tabs and returns.
if {[regexp {[\t\r\n]} $newurl]} {
alertnote "$msg1 contains tabs or returns. It will not be added to the URL cache."
return
}
if {[string length $newurl]} {
htmlAddToCache URLs $newurl
message "$newurl added to URLs."
} else {
beep
message $msg2
}
}
proc htmlAddSelection {} {
htmlSelScrapToURL Select Selection "No selection!"
}
proc htmlAddClipboard {} {
htmlSelScrapToURL Scrap Clipboard "Clipboard empty!"
}
proc htmlClearCache {cache} {
global HTMLmodeVars modifiedModeVars
if {[askyesno "Remove all $cache from [string range $cache 0 [expr [string length $cache] - 2]] cache?"] == "yes"} {
set HTMLmodeVars($cache) {}
lappend modifiedModeVars [list $cache HTMLmodeVars]
htmlEnable$cache off
}
}
# Imports all URLs in a file to the cache.
proc htmlImport {} {
global HTMLmodeVars modifiedModeVars htmlURLAttr
set urls $HTMLmodeVars(URLs)
if {[catch {getfile "Import URLs from:"} fil] || ![htmlIsTextFile $fil alertnote]} {return}
set fid [open $fil r]
set filecont " [read $fid]"
close $fid
if {[llength $urls]} {
set cl [askyesno -c "Clear URL cache before importing?"]
if {$cl == "cancel"} {
return
} elseif {$cl == "yes"} {
set urls {}
}
}
set exp1 "\[ \\t\\n\\r\]+("
foreach attr $htmlURLAttr {
append exp1 "$attr|"
}
set exp1 [string trimright $exp1 |]
append exp1 ")(\"\[^\">\]+\"|\[^ \\t\\n\\r>\]+)"
set exp2 {[ \t\r\n]+(url)\(\"?([^\"\)]+)\"?\)}
for {set i1 1} {$i1 < 3} {incr i1} {
set fcont $filecont
set exp [set exp$i1]
while {[regexp -nocase -indices $exp $fcont a b url]} {
set link [htmlURLunEscape [string trim [string range $fcont [lindex $url 0] [lindex $url 1]] \"]]
set fcont [string range $fcont [lindex $url 1] end]
if {[lsearch -exact $urls $link] < 0} {
lappend urls $link
}
}
}
set HTMLmodeVars(URLs) [lsort $urls]
lappend modifiedModeVars {URLs HTMLmodeVars}
htmlEnableURLs [expr ([llength $HTMLmodeVars(URLs)] > 0)]
message "URLs imported."
}
# Export URLs in cache to a file.
proc htmlExport {} {
global HTMLmodeVars
if {![llength $HTMLmodeVars(URLs)]} {
alertnote "URL cache is empty."
return
}
foreach url $HTMLmodeVars(URLs) {
lappend out "HREF=\"$url\""
}
if {![catch {putfile "Export URL cache to:" "URL cache"} fil]} {
if {[file exists $fil]} {removeFile $fil}
set fid [open $fil w]
puts $fid [join $out "\n"]
close $fid
message "URLs exported."
}
}
# Add all files in a folder to URL cache.
proc htmlAddFolder {} {
global HTMLmodeVars modifiedModeVars
if {[catch {htmlGetDir "Folder to cache:"} folder]} {return}
set path ""
foreach hp $HTMLmodeVars(homePages) {
if {[string match "[lindex $hp 0]:*" "$folder:"]} {
set path [string range $folder [expr [string length [lindex $hp 0]] +1] end]
regsub -all {:} $path {/} path
if {[string length $path]} {append path /}
}
}
set val [dialog -w 350 -h 80 -t "Path:" 10 10 60 30 -e $path 70 10 340 25 \
-b OK 20 50 85 70 -b Cancel 110 50 175 70]
if {[lindex $val 2]} {return}
set path [string trim [lindex $val 0]]
if {[string length $path]} {set path "[string trimright $path /]/"}
set urls $HTMLmodeVars(URLs)
if {[llength $urls]} {
set cl [askyesno -c "Clear URL cache first?"]
if {$cl == "cancel"} {
return
} elseif {$cl == "yes"} {
set urls {}
}
}
foreach fil [glob -nocomplain "$folder:*"] {
set name [file tail $fil]
if {![file isdirectory $fil] && [lsearch -exact $urls "$path$name"] < 0} {
lappend urls "$path$name"
}
}
set HTMLmodeVars(URLs) [lsort $urls]
lappend modifiedModeVars {URLs HTMLmodeVars}
htmlEnableURLs [expr ([llength $HTMLmodeVars(URLs)] > 0)]
message "Files added to URL cache."
}
#===============================================================================
# Footers
#===============================================================================
proc htmlFooters {} {
global HTMLmodeVars modifiedModeVars
set footers [lsort $HTMLmodeVars(footers)]
set touchedIt 0
set this ∞
while {1} {
set box "-t {Footers:} 10 10 80 30 \
-t Path: 30 50 80 70 \
-b OK 10 110 75 130 -b Cancel 90 110 155 130 -b New… 170 110 235 130"
if {[llength $footers]} {
set foot ""
foreach f $footers {
lappend foot [file tail $f]
}
append box " -m [list [concat [list $this] $foot]] 90 10 440 30"
append box " -b Remove 250 110 315 130 -b Insert 330 110 395 130"
foreach f $footers {
lappend box -n [file tail $f] -t $f 90 50 440 90
}
} else {
append box " -m {{None defined} {None defined}} 90 10 440 30"
}
set values [eval [concat dialog -w 450 -h 140 $box]]
set this [lindex $values 3]
if {[lindex $values 0]} {
set HTMLmodeVars(footers) $footers
lappend modifiedModeVars {footers HTMLmodeVars}
return
} elseif {[lindex $values 1]} {
if {!$touchedIt || [askyesno "Really cancel without saving changes?"] == "yes"} {return}
} elseif {[lindex $values 2]} {
if {![catch {htmlNewFooter $footers} newfoot]} {
lappend footers $newfoot
set footers [lsort $footers]
set this [file tail $newfoot]
set touchedIt 1
}
} else {
set i [lsearch -exact $foot $this]
set footerFile [lindex $footers $i]
if {[lindex $values 5]} {
if {![catch {readFile $footerFile} footText]} {
insertText "\r$footText\r"
set HTMLmodeVars(footers) $footers
lappend modifiedModeVars {footers HTMLmodeVars}
message "$this inserted."
return
} else {
alertnote "Could not read $this."
}
} else {
set footers [lreplace $footers $i $i]
set touchedIt 1
}
}
}
}
# Define a file as a footer.
proc htmlNewFooter {footers} {
set newFooter [getfile "Select the file with the footer."]
if {![htmlIsTextFile $newFooter alertnote]} {
error ""
} elseif {[lsearch -exact $footers $newFooter] < 0} {
# Can't define two footers with the same file name.
foreach f $footers {
if {[file tail $f] == [file tail $newFooter]} {
alertnote "There is already a footer with the filename\
'[file tail $newFooter]'. Two footers with the same filename\
cannot be defined."
error ""
}
}
return $newFooter
} else {
alertnote "'[file tail $newFooter]' already a footer."
error ""
}
}
#===============================================================================
# Last modified
#===============================================================================
proc htmlInsertLastMod {} {
global HTMLmodeVars
set values [dialog -w 300 -h 190 -t "Last modified tags" 40 10 200 30 \
-e $HTMLmodeVars(lastModified) 10 40 290 55 -t "Date format" 10 70 100 90 \
-r "Long" 1 10 95 70 115 -r "Abbreviated" 0 80 95 180 115 -r "Short" 0 190 95 250 115 \
-c "Include weekday" 0 10 120 150 140 -c "Include time" 0 160 120 290 140 \
-b OK 20 160 85 180 -b Cancel 110 160 175 180]
if {[lindex $values 7]} {return}
set lm [htmlQuote [lindex $values 0]]
set indent [htmlFindNextIndent]
set text "<!-- [htmlSetCase "#LASTMODIFIED TEXT"]=\"$lm\" [htmlSetCase FORM]=\""
if {[lindex $values 1]} {append text [htmlSetCase LONG]}
if {[lindex $values 2]} {append text [htmlSetCase ABBREV]}
if {[lindex $values 3]} {append text [htmlSetCase SHORT]}
if {[lindex $values 4]} {append text [htmlSetCase ",WEEKDAY"]}
if {[lindex $values 5]} {append text [htmlSetCase ",TIME"]}
append text "\" -->"
set text "$text\r$indent[htmlGetLastMod $text]\r$indent<!-- [htmlSetCase /#LASTMODIFIED] -->"
if {![catch {search -s -f 1 -r 1 -m 0 -i 1 {<!--[ \t\r]+#LASTMODIFIED[ \t\r]+[^>]+>} 0} res] &&
![catch {search -s -f 1 -r 1 -m 0 -i 1 {<!--[ \t\r]+/#LASTMODIFIED[ \t\r]+[^>]+>} [lindex $res 1]} res2]} {
if {[askyesno "There are already 'last modified' tags in this document. Replace them?"] == "yes"} {
replaceText [lindex $res 0] [lindex $res2 1] $text
}
} else {
insertText [htmlOpenCR $indent 1] $text "\r$indent\r$indent"
}
}
# ◊◊◊◊ Change below for new system §5 ◊◊◊◊ #
proc htmlLastModified {name} {
# ◊◊◊◊ end changing for new system §5 ◊◊◊◊ #
if {[lindex [winNames -f] 0] != $name} {bringToFront $name}
if {![catch {search -s -f 1 -r 1 -m 0 -i 1 {<!--[ \t\r]+#LASTMODIFIED[ \t\r]+[^>]+>} 0} res]} {
if {[catch {search -s -f 1 -r 1 -m 0 -i 1 {<!--[ \t\r]+/#LASTMODIFIED[ \t\r]+[^>]+>} [lindex $res 1]} res2]} {
alertnote "The window '[file tail $name]' contains an opening 'last modified' tag without a matching closing tag."
return
}
set str [htmlGetLastMod [getText [lindex $res 0] [expr [lindex $res 1] + 1]]]
if {$str == "0"} {
alertnote "The window '[file tail $name]' contains invalid 'last modified' tags."
} else {
set indent [htmlFindIndent [lindex $res 0]]
replaceText [lindex $res 1] [lindex $res2 0] "\r" $indent $str "\r" $indent
}
}
}
proc htmlGetLastMod {str} {
global htmlSpecialCharacter htmlSpecialCapCharacter
set text ""
set form ""
set type ""
if {![regexp -nocase {TEXT=\"([^\"]*)\"} $str dum text] ||
![regexp -nocase {FORM=\"([^\"]*)\"} $str dum form] || $form == "" ||
![regexp -nocase {[^,]*} $form type] ||
[lsearch -exact [list LONG ABBREV SHORT] [string toupper $type]] < 0} {return 0}
set text [htmlUnQuote $text]
set day [string match "*WEEKDAY*" [string toupper $form]]
set tid [string match "*TIME*" [string toupper $form]]
set date [mtime [now] [string tolower $type]]
if {!$day && [string toupper $type] != "SHORT"} {
set date [lreplace $date 0 0 [lrange [lindex $date 0] 1 end]]
}
if {!$tid} {
set date [lindex $date 0]
} else {
set tiden [lindex $date 1]
regexp {^[0-9]+[^0-9]+[0-9]+} $tiden tidstr
set tiden [lreplace $tiden 0 0 $tidstr]
set date [lreplace $date 1 1 $tiden]
}
set text "$text [join $date]"
regsub -all "&" $text "\\&" text
regsub -all "<" $text "\\<" text
regsub -all ">" $text "\\>" text
regsub -all "¿" $text "\\¿" text
regsub -all "¡" $text "\\¡" text
foreach c [array names htmlSpecialCharacter] {
regsub -all $c $text "\\&$htmlSpecialCharacter($c);" text
}
foreach c [array names htmlSpecialCapCharacter] {
regsub -all $c $text "\\&$htmlSpecialCapCharacter($c);" text
}
foreach c [list eth ETH thorn THORN] {
regsub -all "&$c;" $text $c text
}
return $text
}
#===============================================================================
# Home page windows
#===============================================================================
proc htmlOpenHPwin {{folder ""}} {
global htmlHomePageWinList
# Get folder to open.
if {$folder == "" && [catch {htmlGetDir "Open:"} folder]} {return}
set tail [file tail $folder]
# Is their already a window for this folder?
foreach win $htmlHomePageWinList {
if {[lindex $win 0] == $folder} {
bringToFront [lindex $win 1]
return
}
}
if {[catch {glob $folder:*} fileList]} {beep; message "Empty folder."; return}
set text "$folder\rcmd-shift-C to copy URL\r"
foreach fil $fileList {
append text [file tail $fil] \r
}
if {[set winsize [htmlGetHPwinSize $folder]] == ""} {
new -n $tail
} else {
eval new -n [list "$tail"] -g $winsize
}
insertText $text
if {$winsize == ""} {shrinkWindow 1}
# make folders boldface
for {set i 0} {$i < [llength $fileList]} {incr i} {
set fil [lindex $fileList $i]
if {[file isdirectory $fil]} {
insertColorEscape [rowColToPos [expr $i + 3] 0] bold
insertColorEscape [rowColToPos [expr $i + 4] 0] 12
}
}
htmlSetWin Home
lappend htmlHomePageWinList [list $folder [lindex [winNames] 0]]
}
# Reads a saved home page window size.
proc htmlGetHPwinSize {folder} {
global PREFS htmlHPwinPositions
if {[info exists htmlHPwinPositions($folder)]} {return $htmlHPwinPositions($folder)}
if {![file exists "$PREFS:HTML:Home page window positions"]} {return}
set cid [scancontext create]
set pos ""
scanmatch $cid "^\{?$folder\[ \}\]" {
if {[lindex $matchInfo(line) 0] == $folder} {set pos [lrange $matchInfo(line) 1 end]}
}
set fid [open "$PREFS:HTML:Home page window positions"]
scanfile $cid $fid
close $fid
scancontext delete $cid
return $pos
}
proc htmlQuitHook {} {
global PREFS htmlHPwinPositions
if {![info exists htmlHPwinPositions]} {return}
message "Saving home page window positions…"
set current ""
if {[file exists "$PREFS:HTML:Home page window positions"] &&
![catch {open "$PREFS:HTML:Home page window positions"} fid]} {
set current [split [read -nonewline $fid] \n]
close $fid
}
foreach c $current {
if {[info exists htmlHPwinPositions([lindex $c 0])]} {
append n [lrange $c 0 0] " " $htmlHPwinPositions([lindex $c 0]) \n
unset htmlHPwinPositions([lindex $c 0])
} else {
append n $c \n
}
}
foreach c [array names htmlHPwinPositions] {
append n [list $c] " " $htmlHPwinPositions($c) \n
}
if {![catch {open "$PREFS:HTML:Home page window positions" w} fid]} {
puts -nonewline $fid $n
close $fid
}
}
# ◊◊◊◊ Change below for new system §6 ◊◊◊◊ #
if {![info exists quitHooks] || [lsearch -exact $quitHooks htmlQuitHook] < 0} {
lappend quitHooks htmlQuitHook
}
# ◊◊◊◊ end changing for new system §6 ◊◊◊◊ #
# Quick search in home page windows just like in Finder windows.
proc htmlSearchInHPwin {char} {
global homeTime hpWinString
set t [ticks]
if {[expr $t - $homeTime] > 60} {set hpWinString ""}
append hpWinString $char
set homeTime $t
if {[catch {search -s -f 1 -m 0 -r 1 -i 1 "^$hpWinString" [nextLineStart [nextLineStart 0]]} res]} {return}
select [lindex $res 0] [nextLineStart [lindex $res 1]]
}
proc htmlHomeReturn {} {
global htmlHomePageWinList HTMLmodeVars
foreach win $htmlHomePageWinList {
if {[lindex [winNames] 0] == [lindex $win 1]} {
set f [htmlGetAhpLine]
if {![file exists $f]} {alertnote "[file tail $f] not found."; return}
if {[file isdirectory $f]} {
htmlOpenHPwin $f
} else {
getFileInfo $f a
if {$a(type) == "TEXT"} {
edit -c $f
} elseif {$HTMLmodeVars(homeOpenNonTextFile)} {
if {$a(type) == "APPL"} {
launch -f $f
} elseif {$a(creator) == "MACS"} {
beep; message "Cannot open."
} else {
launchDoc $f
}
} else {
beep; message "Not a text file."
}
}
return
}
}
}
proc htmlHpWinBack {} {
global htmlHomePageWinList
foreach win $htmlHomePageWinList {
if {[lindex [winNames] 0] == [lindex $win 1]} {
set folder [file dirname [getText 0 [expr [nextLineStart 0] - 1]]]
if {$folder != ""} {htmlOpenHPwin $folder}
return
}
}
}
proc htmlGetAhpLine {} {
return "[getText 0 [expr [nextLineStart 0] - 1]]:[getText [lineStart [getPos]] [expr [nextLineStart [getPos]] - 1]]"
}
# Refreshes a Home page window.
proc htmlRefreshHpWin {{hpwin ""}} {
global htmlHomePageWinList
if {$hpwin == ""} {
foreach win $htmlHomePageWinList {
if {[lindex [winNames] 0] == [lindex $win 1]} {
set hpwin $win
}
}
}
set curSel [file tail [htmlGetAhpLine]]
set folder [lindex $hpwin 0]
setWinInfo read-only 0
if {![file exists ${folder}:] || [catch {glob $folder:*} files]} {killWindow; return}
set len [llength $files]
set pos [nextLineStart [nextLineStart 0]]
set ind 0
while {$pos < [maxPos] && $ind < $len} {
set f [file tail [lindex $files $ind]]
set t [string trim [getText $pos [nextLineStart $pos]]]
while {$pos < [maxPos] && $ind < $len && $t == $f} {
incr ind
set pos [nextLineStart $pos]
set f [file tail [lindex $files $ind]]
set t [string trim [getText $pos [nextLineStart $pos]]]
}
if {[string compare [string tolower $t] [string tolower $f]] == 1} {
goto $pos
insertText $f \r
if {[file isdirectory [lindex $files $ind]]} {
insertColorEscape $pos bold
if {![file isdirectory [lindex $files [expr $ind + 1]]]} {
insertColorEscape [nextLineStart $pos] 12
}
} elseif {[file isdirectory [lindex $files [expr $ind + 1]]]} {
insertColorEscape $pos 12
insertColorEscape [nextLineStart $pos] bold
}
set pos [nextLineStart $pos]
incr ind
} else {
deleteText $pos [nextLineStart $pos]
}
if {$pos < [maxPos]} {set t [string trim [getText $pos [nextLineStart $pos]]]}
set f [file tail [lindex $files $ind]]
}
if {$pos < [maxPos]} {
deleteText [expr $pos - 1] [maxPos]
} else {
goto [maxPos]
foreach f [lrange $files $ind end] {
insertText [file tail $f] \r
if {[file isdirectory $f]} {
insertColorEscape $pos bold
insertColorEscape [nextLineStart $pos] 12
}
set pos [nextLineStart $pos]
}
}
htmlRedraw
setWinInfo dirty 0
setWinInfo read-only 1
beginningOfBuffer
if {![catch {search -s -f 1 -m 0 -r 1 -- "^$curSel" 0} res]} {
select [lindex $res 0] [nextLineStart [lindex $res 1]]
}
}
proc htmlRefreshWindows {} {
global htmlHomePageWinList
set frontWin [lindex [winNames -f] 0]
foreach win $htmlHomePageWinList {
bringToFront [lindex $win 1]
htmlRefreshHpWin $win
}
bringToFront $frontWin
}
# Copies an URL from a home page window.
proc htmlCopyURL {} {
global htmlHomePageWinList htmlHomePageWinURL
foreach win $htmlHomePageWinList {
if {[lindex [winNames] 0] == [lindex $win 1]} {
set htmlHomePageWinURL [htmlGetAhpLine]
message "$htmlHomePageWinURL copied."
}
}
}
# Pastes a previously copied URL from a home page window.
proc htmlPasteURL {} {
global htmlHomePageWinURL htmlIsSel htmlCurSel HTMLmodeVars
if {![info exists htmlHomePageWinURL]} {message "No URL to paste."; return}
if {[set link [htmlGetFile $htmlHomePageWinURL 2]] == ""} {return}
set url [htmlURLescape2 [lindex $link 0]]
htmlGetSel
set absPos [getPos]
set htmlWrapPos [lindex [posToRowCol [getPos]] 1]
if {[llength [set wh [lindex $link 1]]]} {
set text [htmlSetCase <IMG]
append text [htmlWrapTag "[htmlSetCase SRC=]\"$url\""]
append text [htmlWrapTag [htmlSetCase "WIDTH=\"[lindex $wh 0]\""]]
append text [htmlWrapTag [htmlSetCase "HEIGHT=\"[lindex $wh 1]\">"]]
set closing ""
} else {
set text "<[htmlSetCase A]"
append text [htmlWrapTag [htmlSetCase HREF=]\"$url\">]
set closing [htmlCloseElem A]
if {!$htmlIsSel && $HTMLmodeVars(useTabMarks)} {append closing "•"}
}
append text $htmlCurSel
set currpos [expr [getPos] + [string length $text]]
append text $closing
if {$htmlIsSel} { deleteSelection }
insertText $text
if {!$htmlIsSel} {
goto $currpos
}
}
# ◊◊◊◊ Change below for new system §7 ◊◊◊◊ #
# Redefines closeHook
if {[info commands htmlCloseHook] == ""} {
rename closeHook htmlCloseHook
proc closeHook {name} {
global htmlHomePageWinList winModes
set winmode $winModes($name)
# First do the normal thing.
htmlCloseHook $name
if {$winmode != "Home"} {return}
set tmp ""
foreach win $htmlHomePageWinList {
if {$name != [lindex $win 1]} {
lappend tmp $win
}
}
set htmlHomePageWinList $tmp
}
}
# Redefines deactivateHook
if {[info commands htmldeactivateHook] == ""} {
rename deactivateHook htmldeactivateHook
proc deactivateHook {name} {
global winModes
htmldeactivateHook $name
if {$winModes($name) != "Home"} {return}
global htmlHPwinPositions
set winSize [getGeometry]
# When closing size is {0 0 0 0}
if {$winSize == {0 0 0 0}} {return}
set htmlHPwinPositions([string trim [getText 0 [nextLineStart 0]]]) $winSize
}
}
# ◊◊◊◊ end changing for new system §7 ◊◊◊◊ #
proc HomeDblClick {from to} {htmlHomeReturn}
foreach __char {a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 . _ -} {
bind '$__char' "htmlSearchInHPwin $__char" Home
}
unset __char
bind '\r' htmlHomeReturn Home
bind down <c> htmlHomeReturn Home
bind enter htmlHomeReturn Home
bind down downBrowse Home
bind up upBrowse Home
bind '\r' <c> htmlHpWinBack Home
bind enter <c> htmlHpWinBack Home
bind up <c> htmlHpWinBack Home
bind 'r' <c> htmlRefreshHpWin Home
bind 'c' <cs> htmlCopyURL Home
#===============================================================================
# Validation
#===============================================================================
proc htmlFindUnbalancedTags {} {
global tileLeft tileTop tileWidth errorHeight htmlPackageToUse
message "Searching for unbalanced tags…"
set fil [stripNameCount [lindex [winNames -f ] 0]]
# These may not have an closing tag.
set empty {!DOCTYPE BASEFONT BR AREA LINK IMG PARAM HR INPUT ISINDEX BASE META}
if {$htmlPackageToUse == 1} {lappend empty COL FRAME SPACER WBR EMBED BGSOUND KEYGEN}
# These have an optional closing tag.
set closingOptional {P DT DD LI OPTION TR TD TH HEAD BODY HTML WINDOW}
if {$htmlPackageToUse == 1} {lappend closingOptional COLGROUP THEAD TBODY TFOOT}
# These have an optional opening tag.
set openingOptional {HTML HEAD BODY}
if {$htmlPackageToUse == 1} {lappend openingOptional TBODY}
set tagStack WINDOW
set pos 0
while {![catch {search -s -f 1 -r 1 -i 1 -m 0 {<!--|<[^<>]+>} $pos} res]} {
set tagstart [lindex $res 0]
set tagend [lindex $res 1]
set tagtxt [getText $tagstart $tagend]
if {$tagtxt == "<!--"} {
# Comment
if {![catch {search -s -f 1 -r 0 -m 0 -- {-->} $tagstart} res]} {
set pos [lindex $res 1]
} else {
set pos [maxPos]
}
continue
}
# get element name
if {![regexp {<[ \t\r]*([^ \t\r]+).*>} $tagtxt tmp tag]} {
append errtxt "Line [lindex [posToRowCol $tagstart] 0]: Empty <>.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
set pos $tagend
continue
}
set tag [string toupper $tag]
# is this a closing tag?
if {[string index $tag 0] == "/"} {
set tag [string range $tag 1 end]
if {[lsearch -exact $empty $tag] >= 0} {
append errtxt "Line [lindex [posToRowCol $tagstart] 0]: $tag may mot have a closing tag.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
} elseif {[lsearch -exact $tagStack $tag] < 0 && [lsearch -exact $openingOptional $tag] < 0} {
append errtxt "Line [lindex [posToRowCol $tagstart] 0]: Closing $tag without a matching opening tag.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
} else {
for {set i 0} {$i < [llength $tagStack]} {incr i} {
if {[set this [lindex $tagStack $i]] != $tag} {
if {[lsearch -exact $closingOptional $this] < 0} {
append errtxt "Line [lindex [posToRowCol $tagstart] 0]: $this must be closed before $tag.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
}
} else {
break
}
}
set tagStack [lrange $tagStack [expr $i + 1 ] end]
}
} else {
# opening tag
if {[lsearch -exact $empty $tag] < 0} {
set tagStack [concat $tag $tagStack]
}
}
set pos $tagend
}
# check if there are unclosed tags.
for {set i 0} {$i < [llength $tagStack]} {incr i} {
if {[lsearch -exact $closingOptional [set this [lindex $tagStack $i]]] < 0} {
append errtxt "Line [lindex [posToRowCol [maxPos]] 0] : $this must be closed before HTML.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
}
}
if {[info exists errtxt]} {
new -n "* Errors *" -g $tileLeft $tileTop $tileWidth $errorHeight
insertText "Errors: (<uparrow> and <downarrow> to browse, <return> to go to file)\r\r"
insertText $errtxt
htmlSetWin Brws
} else {
alertnote "No unbalanced tags found!"
}
}
proc htmlCheckTags {} {
global tileLeft tileTop tileWidth errorHeight htmlPackageToUse
message "Checking tags…"
set fil [stripNameCount [lindex [winNames -f ] 0]]
eval htmlCheckConfig$htmlPackageToUse
# Validate
set headHasBeen 0
set bodyHasBeen 0
set htmlHasBeen 0
set tagStack WINDOW
set currentTag WINDOW
set pos 0
while {![catch {search -s -f 1 -r 1 -i 1 -m 0 {<!--|<[^<>]+>} $pos} res]} {
set tagstart [lindex $res 0]
set tagend [lindex $res 1]
set tagtxt [getText $tagstart $tagend]
# get element name
if {$tagtxt != "!--" && ![regexp {<[ \t\r]*([^ \t\r>]+)} $tagtxt tmp tag]} {
append errtxt "Line [lindex [posToRowCol $tagstart] 0]: Empty <>.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
set pos $tagend
continue
} else {
set tag [string toupper $tag]
}
if {$tagstart > $pos} {
set prevTxt [getText $pos [expr $tagstart -1]]
} else {
set prevTxt ""
}
# check for unmatched < or > in text.
if {[regexp {<} $prevTxt]} {
append errtxt "Line [lindex [posToRowCol $tagstart] 0]: Unmatched <.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
}
if {[regexp {>} $prevTxt]} {
append errtxt "Line [lindex [posToRowCol $tagstart] 0]: Unmatched >.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
}
# check for text if current element may not contain text.
set back 0
if {[lsearch -exact $mayContain($currentTag) text] < 0 &&
![regexp {^[ \t\r]*$} $prevTxt ]} {
# back up and insert BODY if needed
if {!$bodyHasBeen && [lsearch -exact $tagStack BODY] < 0 &&
!($htmlPackageToUse == 1 && [lsearch -exact $tagStack FRAMESET] >= 0)} {
set tagend $pos
set tag BODY
set back 1
} else {
append errtxt "Line [lindex [posToRowCol $tagstart] 0]: $currentTag may not contain text.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
}
}
if {!$back && $tagtxt == "<!--"} {
# Comment
if {![catch {search -s -f 1 -r 0 -m 0 -- {-->} $tagstart} res]} {
set pos [lindex $res 1]
} else {
set pos [maxPos]
}
continue
}
# Silently ignore !DOCTYPE
if {$tag == "!DOCTYPE"} {
set pos $tagend
continue
}
# back up and insert HEAD if needed.
if {!$headHasBeen && [lsearch -exact $mayContain(HEAD) $tag] >= 0} {
set tagend $pos
set tag HEAD
}
# back up and insert TBODY if needed
if {$htmlPackageToUse == 1 && $currentTag == "TABLE" && [lsearch -exact $mayContain(TABLE) $tag] < 0} {
set tagend $pos
set tag TBODY
}
set xtag [string trimleft $tag /]
# insert BODY if tag can't be in HEAD or HEAD is closed.
if {!$bodyHasBeen && ([lsearch -exact $mayContain(HEAD) $xtag] < 0 ||
[lsearch -exact $tagStack HEAD] < 0) &&
$xtag != "HTML" && $xtag != "HEAD" && $xtag != "BODY" &&
!($htmlPackageToUse == 1 && $xtag == "FRAMESET" || [lsearch -exact $tagStack FRAMESET] >= 0)} {
set tagend $pos
set tag BODY
}
# insert HTML if not done
if {!$htmlHasBeen && $tag != "HTML"} {
set tagend $pos
set tag HTML
}
# check if there's anything after </HTML>
if {$tag == "/HTML"} {
if {![regexp {^[ \t\r]*$} [getText $tagend [maxPos]]]} {
append errtxt "Line [lindex [posToRowCol $tagstart] 0]: Text after </HTML>.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
}
break
}
# is this a closing tag?
if {[string index $tag 0] == "/"} {
set tag [string range $tag 1 end]
if {![info exists mayContain($tag)]} {
append errtxt "Line [lindex [posToRowCol $tagstart] 0]: $tag is unknown.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
} else {
if {$tag == "HEAD" || $tag == "BODY"} {set headHasBeen 1}
if {$tag == "BODY"} {set bodyHasBeen 1}
if {[lsearch -exact $empty $tag] >= 0} {
append errtxt "Line [lindex [posToRowCol $tagstart] 0]: $tag may mot have a closing tag.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
} elseif {[lsearch -exact $tagStack $tag] < 0} {
append errtxt "Line [lindex [posToRowCol $tagstart] 0]: Closing $tag without a matching opening tag.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
} else {
for {set i 0} {$i < [llength $tagStack]} {incr i} {
if {[set this [lindex $tagStack $i]] != $tag} {
if {[lsearch -exact $closingOptional $this] < 0} {
append errtxt "Line [lindex [posToRowCol $tagstart] 0]: $this must be closed before $tag.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
}
} else {
break
}
}
set tagStack [lrange $tagStack [expr $i + 1 ] end]
set currentTag [lindex $tagStack 0]
}
}
} else {
# opening tag
if {$headHasBeen && $tag == "HEAD"} {
append errtxt "Line [lindex [posToRowCol $tagstart] 0]: Multiple HEAD tags.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
}
if {$bodyHasBeen && $tag == "BODY"} {
append errtxt "Line [lindex [posToRowCol $tagstart] 0]: Multiple BODY tags.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
}
if {$htmlHasBeen && $tag == "HTML"} {
append errtxt "Line [lindex [posToRowCol $tagstart] 0]: Multiple HTML tags.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
}
if {$tag == "HEAD" || $tag == "BODY"} {set headHasBeen 1}
if {$tag == "BODY"} {set bodyHasBeen 1}
if {$tag == "HTML"} {set htmlHasBeen 1}
# unknown tag?
if {[set em [lsearch -exact $empty $tag]] < 0 && ![info exists mayContain($tag)]} {
append errtxt "Line [lindex [posToRowCol $tagstart] 0]: $tag is unknown.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
} else {
# implicitely close those which may not contain $tag.
for {set i 0} {$i < [llength $tagStack]} {incr i} {
set this [lindex $tagStack $i]
if {[lsearch -exact $mayContain($this) $tag] < 0 || [lsearch -exact $form $tag] >= 0 && [lsearch -exact $tagStack FORM] < 0} {
# Silently close those with an optional closing tag except BODY and HTML.
if {[lsearch -exact $closingOptional $this] < 0 || $this == "BODY" || $this == "HTML"} {
append errtxt "Line [lindex [posToRowCol $tagstart] 0]: $this may not contain $tag.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
break
}
} else {
break
}
}
if {$em < 0} {
set tagStack [concat $tag [lrange $tagStack $i end]]
set currentTag $tag
} else {
set tagStack [lrange $tagStack $i end]
}
}
}
set pos $tagend
}
# check if there are unclosed tags.
for {set i 0} {$i < [llength $tagStack]} {incr i} {
if {[lsearch -exact $closingOptional [set this [lindex $tagStack $i]]] < 0} {
append errtxt "Line [lindex [posToRowCol [maxPos]] 0] : $this must be closed before HTML.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$fil\r"
}
}
if {[info exists errtxt]} {
new -n "* Errors *" -g $tileLeft $tileTop $tileWidth $errorHeight
insertText "Errors: (<uparrow> and <downarrow> to browse, <return> to go to line)\r\r"
insertText $errtxt
htmlSetWin Brws
} else {
alertnote "No syntax errors found! (Attributes have not been checked.)"
}
}